KIPR's Instructional Software System (KISS for short) is an integrated development environment providing an editor, compilers for multiple programming languages, and a set of libraries and simulator for the LINK Botball Controller. KISS implements the full ANSI C specification. For information about the C programing language, including history and basic syntax, see the Wikipedia article C (programming language). For a more complete tutorial and guide for C Programming visit CPrograming. The Botball community website also has several articles about programming and a user forum where questions can be posted to the botball community. For specific information on Motors and Sensors, see the Sensors and Motors Manual
The primary purpose of this manual is to describe the KIPR Link Botball Controller libraries and simulator, which are extensions to the C programming language. This file also provides a basic introduction to programming in C. To learn more about programming in C, consult one of the many books or websites that provide C references and tutorials.
Both new (unsaved) and saved files can be opened for editing in KISS. A row of tabs lists the files that have been opened. Clicking a file's tab activates it for editing.
The File menu has standard entries for New, Open, Save, Save As, Print, Close and Exit.
To use the simulator, simply select Simulator as the target for KISS. Clicking Compile will compile and run the program in the simulator. The active file will also be saved, unless it is new, in which case the user is prompted for a "save as" file name. The active file must contain (or #include) the main function in order to run in the simulator.
When your Link is cabled to your computer, its name will appear as one of the available targets. Now clicking Compile will compile and download your program to your Link, where it can be run from the Link's Programs tab.
The interface provides additional capabilities for program entry/edit, minor adjustment to the display (font size), auto-completion and auto-indentation. If there is a syntax error in the program, an error window will appear at the bottom of the KISS screen with the error message and the file and line number where the error was detected. If the error occurred in the active file, then clicking on the error message will move to that line. The edit menu provides a Go To line option.
C programs are automatically formatted and indented. Keywords, comments, and text strings are highlighted with color unless this feature is turned off. Most Link-specific library functions will have pop-help when they are entered into the input window.
KISS does parenthesis-balance-highlighting when the cursor is placed to the right of any right parenthesis, bracket, or brace.
Most C programs consist of function definitions and data structures. Here is a simple C program that defines a single function, called main.
/* Simple example: C Programmer's Manual */
int main()
{
printf("Hello, world!/n");// simple example
}
For a C program to be executable, one of its functions must be named main.
The Expression
/* <text> */
forms a multi-line or bracketed comment. In contrast, text that
starts with
//
forms a single line comment,
which continues only to the end of the line. Comments are ignored by C when the
program is compiled.
All functions must have a return type. While main does not return a value to another function, it does return an integer to the operating system, and so is of type int. Other types include no return value (void) and floating point numbers are best specified by using (double). This function declaration information must precede each function definition.
Immediately following the function declaration is the function's name (in this case,main). Next, in parentheses, are any arguments (or inputs) to the function. main has none, but an empty set of parentheses is still required.
After the function arguments is an open curly-brace {. This signifies the start of the actual function code. Curly-braces signify program blocks, or chunks of code.
Next comes a series of C statements. Statements demand that some action be taken. Our demonstration program has a single statement, a printf ("Hello, world!/n"). This will print the message "Hello, world!" to the KIPR Link display. The \n indicates start-of-a-new-line (in effect ending the current line and positioning for the next print to start on the next line). When the bottom of the display is reached, additional lines cause the display to scroll up. The printf statement ends with a semicolon (;). All C statements must be ended by a semicolon. Beginning C programmers commonly make the error of omitting the semicolon that is required to end each statement.
The main function is ended by the close curly-brace }.
Let's look at another example to learn some more features of C. The following code defines the function square, which returns the mathematical square of a number.
int square(int n)
{
return(n * n);
}
The function is declared as type int, which means that it will return an integer value.
Next comes the function named square, followed by its argument list in parentheses. Square has one argument, n, which is an integer. Notice how declaring the type of the argument is done similarly to declaring the type of the function.
When a function has arguments declared, those argument variables are valid within the "scope" of the function (i.e., they only have meaning within the function's own code). Other functions may use the same variable names independently.
The code, or "scope", for square is contained within the set of curly braces. In fact, it consists of a single statement: the return statement. The return statement exits the function and returns the value of the C expression that follows it (in this case "n * n").
Except where grouped by parentheses, expressions are evaluated according to a set of precedence rules associated with the various operations within the expression. In this case, there is only one operation (multiplication), signified by the "*", so precedence is not an issue.
Let's look at an example of a function that performs a function call to the square program.
double hypotenuse(int a, int b)
{
double h;
h = sqrt((double)(square(a) + square(b)));
return(h);
}
This code demonstrates several more features of C. First, notice that the floating point variable h is defined at the beginning of the hypotenuse function. In general, whenever a new program block (indicated by a set of curly braces) is begun, new local variables may be defined.
The value of h is set to the result of a call to the sqrt function, which is included from the C math library by KISS. sqrt is a built-in C function that takes a floating point number as its argument.
We want to use the square function we defined earlier, which returns its result as an integer. But the sqrt function requires a floating point argument. We get around this type incompatibility by coercing the integer sum (square(a) + square(b)) into a float by preceding it with the desired type, in parentheses. Thus, the integer sum is made into a floating point number and passed along to sqrt.
The hypotenuse function finishes by returning the value of h.
Note that the functions square and hypotenuse are functions, not a program. If they are in the same file as the main function written above, they will never be called. If main was modified to be:
/* Simple example: C Programmer's Manual */
int main()
{
printf("Hypotenuse of 3,4 is %d\n",hypotenuse(3,4)); // call a function
}
then the program (which always starts execution with main) would calculate and print out the hypotenuse of a triangle with side lengths of 3 & 4. However, C requires that functions be defined before they are first called. Since hypotenuse is called in main, either the functions need to be defined earlier in the file, or they can be prototyped at the begining of the file. A prototype of a C function is simply a C statement that includes the return type, function name and types of the parameters. For example the prototypes of the functions square and hypotenuse would be:
int square(int n);
double hypotenuse(int a, int b);
The order in which function protypes appear does not matter as long as they come before the definitions that call that function.
This concludes the brief C tutorial.
Variables and constants are the basic data objects in a C program. Declarations list the variables to be used, state what type they are, and may set their initial value.
Variable names are case-sensitive. The underscore character is allowed and is often used to enhance the readability of long variable names. C keywords like if, while, etc. may not be used as variable names.
Functions and global variables may not have the same name. In addition, if a local variable is named the same as a function or a global variable, the local use takes precedence; ie., use of the function or global variable is prevented within the scope of the local variable.
In C, variables can be declared at the top level (outside of any curly braces) or at the start of each block (a functional unit of code surrounded by curly braces). In general, a variable declaration is of the form:
<type> <variable-name>; or |
<type> <variable-name>=<initialization-data>; |
If a variable is declared within a function, or as an argument to a function, its binding is local, meaning that the variable has existence only within that function definition. If a variable is declared outside of a function, it is a global variable. It is defined for all functions, including functions which are defined in files other than the one in which the global variable was declared.
Local and global variables can be initialized to a value when they are declared. If no initialization value is given, their value is indeterminate.
All global variable declarations must be initialized to constant values. Local variables may be initialized to the value of arbitrary expressions including any global variables, function calls, function arguments, or local variables which have already been initialized.
Here is a small example of how initialized declarations are used.
int i=50; /* declare i as global integer; initial value 50 */
double j=100.123; /* declare j as global double; initial value 100.123 */
int foo()
{
int x; /* declare x as local integer; initial value 0 */
double y=j; /* declare y as local double; initial value j */
}
Local variables are initialized whenever the function containing them is executed. Global variables are initialized whenever a reset condition occurs. Reset conditions occur when:
Integers constants may be defined in decimal integer format (e.g., 4053 or -1) or hexadecimal format using the "0x" prefix (e.g., 0x1fffffff).
Floating point numbers may use exponential notation (e.g., "10e3" or "10E3") or may contain a decimal period. For example, the floating point zero can be given as "0.", "0.0", or "0E1", but not as just "0". Since the board has no floating point hardware, floating point operations are handled by software, making them much slower than integer operations. Hence,floating point should only be used for data that is inherently fractional.
Quoted characters return their ASCII value (e.g., 'x').
Character string constants are defined with quotation marks, e.g., "This is a character string.".
The special constant NULL can be assigned to and compared to pointer or array variables (which will be described in later sections). In general, a pointer represents a memory location. A NULL pointer represents the case the pointer points to nothing.
To check if a pointer variable has been been assigned you compare its value to NULL. As an example, if you had a defined a linked list type consisting of a value and a pointer to the next element, the end of the list is determined by comparing a list element's pointer variable to NULL.
C supports the following data types among others:
32-bit integers are signified by the type indicator int. They are signed integers, and may be valued from -2,147,483,648 to +2,147,483,647 decimal.
Floating point numbers are best specif by the type indicator double. 64-bit floating point numbers have at least 15 decimal digits of precision and are valued from about 10^-308 to 10^308.
Characters are an 8-bit (unsigned) number signified by the type indicator char. A character's value normally represents a standard ASCII character code, most of which are printable.
C pointers are 32-bit numbers which represent locations in memory. Values in memory can be manipulated by calculating, passing and dereferencing pointers representing the location where the information is stored (derefence means to obtain the value stored at the location).
Arrays are used to store homogenous lists of data (meaning that all the elements of an array have the same type). Every array has a length which is determined at the time the array is declared. The data stored in the elements of an array can be set and retrieved in the same manner as for other variables.
Structures are used to store non-homogenous but related sets of data. Elements of a structure are referenced by name instead of number and may be of any supported type.
Structures are useful for organizing related data into a coherent format, reducing the number of arguments passed to functions, increasing the effective number of values which can be returned by functions, and creating complex data representations such as directed graphs and linked lists.
The address where a value is stored in memory is known as the pointer to that value. It is often useful to deal with pointers to objects, but great care must be taken to insure that the pointers used at any point in your code really do point to valid objects in memory.
For additional information on programming with C pointers, please refer to a C programming book or website.
C supports arrays of characters, integers, floating-point numbers, structures, pointers, and array pointers (multi-dimensional arrays). The main reasons that arrays are useful are that they allow you to allocate space for many instances of a given type, send an arbitrary number of values to functions, and provide the means for iterating over a set of values.
Arrays are declared using square brackets. The following statement declares an array of ten integers:
int foo[10];
this array, elements are numbered from 0 to 9. Elements are accessed by enclosing the index number within square brackets: foo[4] denotes the fifth element of the array foo (since counting begins at zero).
Arrays not initialized at declaration contain indeterminate values. Arrays may be initialized at declaration by specifying the array elements, separated by commas, within curly braces. If no size value is specified within the square brackets when the array is declared but initialization information is given, the size of the array is determined by the number of elements given in the declaration. For example,
int foo[]= {0, 4, 5, -8, 17, 301};
creates an array of six integers, with foo[0] equaling 0, foo[1] equaling 4, etc.
If a size is specified and initialization data is given, the length of the initialization data may not exceed the specified length of the array or an error results. If, on the other hand, you specify the size and provide fewer initialization elements than the total length of the array, the remaining elements contain indeterminate values.
Text strings are implemented as arrays of characters. C provides a syntax for initializing arrays of characters. The character values of the string are enclosed in quotation marks as given in the following example:
char ex_string[]= "Hello there";
This initializes the character array ex_string with the ASCII values of the specified characters. The length of the array is the number of characters in the string plus 1, since strings are automatically terminated by the (unprintable) ASCII character '\0'. The terminator is the means C uses to view the array as a string. C has a library of string functions included by KISS for determining string length, combining strings, and the like (look for string functions in your C reference).
A character array can be initialized using the curly braces syntax, but unless '\0' is included, there is nothing to mark the end of the string, and printing the array as a string will produce indeterminate results. In declaring a character array intended to hold strings, the array size needs to be 1 larger than the size of the maximum string to allow for '\0'. For example, given
char pg_string[81];
When an array is passed to a function as an argument, the array's pointer is actually passed, rather than the elements of the array. If the function modifies the array values, the array will be modified, since there is only one copy of the array in memory.
There are two ways of declaring an array argument: as an array or as a pointer to the type of the array's elements.
As an example, the following function takes an index and an array, and returns the array element specified by the index:
int retrieve_element(int index, int array[])
{
return array[index];
}
Notice the use of the square brackets to declare the argument array as a pointer to an array of integers.
When passing an array variable to a function, you are actually passing the value of the array pointer itself and not one of its elements, so no square brackets are used.
void foo()
{
int array[10];
retrieve_element(3, array);
}
A two-dimensional array is just like a single dimensional array whose elements are one-dimensional arrays. Declaration of a two-dimensional array is as follows:
int k[2][3];
The number in the first set of brackets is the number of 1-D arrays of int. The number in the second set of brackets is the length of each of the 1-D arrays of int. In this example, k is an array containing two 1-D arrays; each of k[0] and k[1] is a 1-D array of length 3 consisting of values of type int. k[0][1] is the value in position 1 of k[0]. Arrays of with any number of dimensions can be generalized from this example by adding more brackets in the declaration.
Structures are used to store non-homogenous but related sets of data. Elements of a structure are referenced by name instead of number and may be of any supported type. Structures are useful for organizing related data into a coherent format, reducing the number of arguments passed to functions, increasing the effective number of values which can be returned by functions, and creating complex data representations such as directed graphs and linked lists.
The following example shows how to define a structure, declare a variable of structure type, and access its elements.
struct foo
{
int i;
int j;
};
struct foo f1;
void set_f1(int i,int j)
{
f1.i=i;
f1.j=j;
}
void get_f1(int *i,int *j)
{
*i=f1.i;
*j=f1.j;
}
The first part is the structure definition. It consists of the keyword struct, followed by the name of the structure (which can be any valid identifier), followed by a list of named elements in curly braces. This definition specifies the structure of the type struct foo. Once there is a definition of this form, you can use the type struct foo just like any other type. The line
struct foo f1;
is a global variable declaration which declares the variable f1 to be of type struct foo.
The dot operator is used to access the elements of a variable of structure type. In this case, f1.i and f1.j refer to the two elements of the struct variable f1. You can treat the quantities f1.i and f1.j just as you would treat any variables of type int (the type of the elements specified in the struct declaration of foo is int).
Pointers to structure types can also be used, just like pointers to any other type. However, with structures, there is a special short-cut for referring to the elements of the structure pointed to.
struct foo *fptr;
void main()
{
fptr=&f1;
fptr->i=10;
fptr->j=20;
}
this example, fptr is declared to be a pointer to type struct foo. In main, it is set to point to the global f1 defined above. Then the elements of the structure pointed to by fptr (in this case these are the same as the elements of f1), are set. The arrow operator is used instead of the dot operator because fptr is a pointer to a variable of type struct foo. Note that (*fptr).i would have worked just as well as fptr->i, but it would have been clumsier.
Note that just as for arrays, only pointers to structures, not the structures themselves, can be passed to or returned from functions.
Complex types -- arrays and structures -- may be initialized upon declaration with a sequence of constant values contained within curly braces and separated by commas.
Arrays of character may also be initialized as a string using a quoted string of characters.
For initialized declarations of single dimensional arrays, the length can be left blank and a suitable length based on the initialization data will be assigned to it. Multi-dimensional arrays must have the size of all dimensions specified when the array is declared. If a length is specified, the initialization data may not overflow that length in any dimension or an error will result. However, the initialization data may be shorter than the specified size and the remaining entries will be initialized to 0.
Following is an example of legal global and local variable initializations:
/* declare many globals of various types */
int i=50;
int *ptr=NULL;
double farr[3]={ 1.2, 3.6, 7.4 };
int tarr[2][4]={ { 1, 2, 3, 4 }, { 2, 4, 6, 8} };
char c[]="Hi there how are you?";
char carr[5][10]={"Hi","there","how","are","you"};
struct bar
{
int i;
int *p;
double j;
} b={5, NULL, 10.5};
struct bar barr[2] = { { 1, NULL, 2.5 }, { 3 } };
/* declare locals of various types */
int foo()
{
int x; /* local variable x with initial value 0 */
int y= tarr[0][2]; /* local variable y with initial value 3 */
int *iptr=&i; /* local pointer to integer
which points to the global i */
int larr[2]={10,20}; /* local array larr
with elements 10 and 20 */
struct bar lb={5,NULL,10.5}; /* local variable of type
struct bar with i=5 and j=10.5 */
char lc[]=carr[2]; /* local string lc with
initial value "how" */
...
}
Operators act upon objects of a certain type or types and specify what is to be done to them. Expressions combine variables and constants to create new values. Statements are expressions, assignments, function calls, or control flow statements which make up C programs.
Each of the data types has its own set of operators that determine which operations may be performed on them.
The following operations are supported on integers:
KISS provides the standard C library of floating point routines. This package includes arithmetic, trigonometric, and logarithmic functions. Since floating point operations are implemented in software, they are much slower than the integer operations and so should be avoided where possible if your programming objective could be affected by overall processing speed.
The following operations are supported on floating point numbers:
|
Character variables hold 8-bit unsigned integers whose values reference standard ASCII codes. If a character variable is referenced in an integer operation, it is automatically coerced into an integer representation for use by the integer operation. When a value is stored into a character variable, it is coerced into an 8-bit character (by truncating the upper bits).
The basic assignment operator is =. The following statement adds 2 to the value of a.
a = a + 2;
The abbreviated form
a += 2;
could also be used to perform the same operation. All of the following binary operators can be used in this fashion:
+ - * / % << >> & ^ |
The increment operator "++" increments the named variable. For example, the construction "a++" is equivalent to " a= a+1" or "a+= 1". A statement that uses an increment operator has a value. For example, the statement
a = 3; printf("a=%d a+1=%d\n", a, ++a);
will display the text "a=3 a+1=4". If the increment operator comes after the named variable, then the value of the statement is calculated after the increment occurs. So the statement
a = 3; printf("a=%d a+1=%d\n", a, a++);
would display "a=3 a+1=3" but would finish with a set to 4. The decrement operator "--" is used in the same fashion as the increment operator.
|
|
|
|
|
The following table summarizes the rules for precedence and associativity for the C operators. Operators listed earlier in the table have higher precedence; operators on the same line of the table have equal precedence.
|
All standard C control structures are available under KISS.
A single C statement is ended by a semicolon. A series of statements may be grouped together into a block using curly braces. Inside a block, local variables may be defined. Blocks may be used in place of statements in the control flow constructs.
For the following, keep in mind a <block> can be used instead of the <statement>.
The if else statement is used to make decisions. The syntax is:
if (<expression>)
<statement-1>
else
<statement-1>
<expression> is evaluated; if it is not equal to zero (e.g., logic true), then <statement-1> is executed.
The else clause is optional. If the if part of the statement did not execute, and the else is present, then <statement-2> executes.
The syntax of a while loop is the following:
while (<expression>)
<statement>
while begins by evaluating <expression>. If it is false, then <statement> is skipped. If it is true, then <statement> is evaluated. Then the expression is evaluated again, and the same check is performed. The loop exits when <expression> becomes zero.
One can easily create an infinite loop in C using the while statement:
while (1)
<statement>
The syntax of a do-while loop is the following:
do
<statement>
while (<expression>);
The equivalent while loop would be the following:
<statement>
while (<expression>)
<statement>
The syntax of a for loop is the following:
for (<expr-1>;<expr-2>;<expr-3>)
<statement>
The for construct is equivalent to the following construct using while:
<expr-1>;
while (<expr-2>)
{
<statement>
<expr-3>;
}
Typically, <expr-1> is an assignment, <expr-2> is a relational expression, and <expr-3> is an increment or decrement of some manner. For example, the following code counts from 0 to 99, printing each number along the way:
int i;
for (i = 0; i < 100; i++)
printf("%d\n", i);
The syntax of a switch block is as follows:
switch (int)
{
case const1:
<statement list1>
break;
case const2:
<statement list2>
break;
default:
<statement list3>
}
The switch construct takes an integer variable as input, and compares it to each case listed. The first matching const is selected, and execution begins there. The break is optional, and if no break is found then execution continues through each following statement. Also note that each case has a list of single statements, as opposed to a block enclosed in curly braces.
Here's an example of how a switch might be used:
int i = 1;
switch(i)
{
case 0:
printf("Case 0\n");
break;
case 1:
printf("Case 1\n");
break;
default:
printf("Default\n");
}
Since i is equal to 1, the text "Case 1" will be printed to the screen. If i were equal to 0, "Case 0" would be printed. If i were any number besides 0 or 1, "Default" would be printed.
Use of the break statement provides an early exit from a while, do-while or for loop. The break statement can also provide an exit from a switch block.
The C function printf provides formatted printing to the KIPR Link display screen. KISS also provides a Link specific print command, display_printf, for controlled screen printing to at a specified (row,column) coordinate on the 10 line by 42 character display (3 button mode only).
The syntax of printf is the following:
printf(<format-string>, <arg-1> , ... , <arg-N>);
The following statement prints a text string to the screen.
printf("Hello, world!\n");
In this example, the format string is simply printed to the screen. The character \n at the end of the string signifies an advance to the next (or new) line for subsequent printing. When the bottom of the display is reached, the display is scrolled (up) for each line subsequently printed.
The following example prints the value of the integer variable x with a brief message.
printf("Value is %d\n", x);
The format %d phrase signals that the first variable in the list is to be formatted as and integer, replacing %d in the printed output.
The following example prints the value of the integer variable x as a hexadecimal number (in caps).
printf("Value is %X\n", x);
The format %X phrase signals that the first variable in the list is to be formatted as hexadecimal value (using uppercase), replacing %X in the printed output.
The following statement prints the value of the floating point variable n as a floating point number.
printf("Value is %f\n", n);
The format %f phrase signals that the first variable in the list is to be formatted as a decimal floating point value (with decimal point), replacing %f in the printed output.
printf("A=%x B=%x\n", a, b);
display_printf(0,2,"A=%d B=%d", a, b);
The function display_printf is like the standard printf function except the first two arguments specify the column and row (zero indexed) to start printing, so for this example, the result is printed on the 3rd row of the display starting from column 0.
Row numbers are 0 to 9, column 0 to 41. Note that display_printf does not wrap, and truncates strings that go beyond the end of the line on the display.
|
The preprocessor processes a file before it is sent to the compiler. The C preprocessor allows definition of macros, and conditional compilation of sections of code. Using preprocessor macros for constants and function macros can make C code more efficient as well as easier to read. Using #if to conditionally compile code can be very useful, for instance, for debugging purposes.
The preprocessor command #include causes the specified file to be included in the code sent to the C compiler at the position the include statement appears in the program. For example, suppose you have a set of stored programs in a file named "mylib.c", some of which you need for your current program to work.
// load my library
#include "mylib.c"
void main()
{
char s[32] = "text string wrapping badly\n";
fix(s);// apply my fix function to s and print it
printf(s);
}
Preprocessor macros are defined by using the #define preprocessor directive at the beginning of a line. The macro will apply to any other files subsequently included in the program. The following example shows how to define preprocessor macros.
#define RIGHT_MOTOR 0
#define LEFT_MOTOR 1
#define GO_RIGHT(power) (motor(RIGHT_MOTOR,(power)))
#define GO_LEFT(power) (motor(LEFT_MOTOR,(power)))
#define GO(left,right) {GO_LEFT(left); GO_RIGHT(right);}
void main()
{
GO(0,0);
}
Preprocessor macro definitions start with the #define directive at the beginning of a line, and continue to the end of the line. The name of the macro follows #define, such as RIGHT_MOTOR (a C programming convention is to use all caps for macro names to make them easy to identify later). If there is a parenthesis directly after the name of the macro, such as the GO_RIGHT macro has above, then the macro has arguments. The GO_RIGHT and GO_LEFT macros each take one argument. The GO macro takes two arguments. The body of the macro follows the name and the optional argument list.
Each time a macro is invoked, any variables are resolved in the body of the macro and the code produced is placed in the program in place of the macro.
Superficially, invocations of macros without arguments look like global variable references and invocations of macros with arguments look like calls to functions. However, macro replacement is occurs as the preprocessor passes through the program before sending it on to the compiler. Global references and function calls happen after compilation at run time. Also, function calls actually evaluate their arguments before they are called, whereas macros simply perform text replacement.
Appropriate use of macros can make C programs easier to read. In particular, constants can be given symbolic names rather than using global variables to provide storage for them. It also allows macros with arguments to be used in cases when a series of function calls is used at multiple points in the program.
It is sometimes desirable to conditionally compile code. The primary example of this is that you may want to have easily supressed debugging output. The C preprocessor provides a convenient way of doing this by using the #ifdef directive.
void go_left(int power)
{
GO_LEFT(power);
#ifdef DEBUG
printf("Going Left\n");
beep();
#endif
}
In this example, when the macro DEBUG is defined, the debugging message "Going Left" will be printed and the board will beep each time the code inserted by GO_LEFT is executed. If the macro is not defined, the preprocessor will not insert the code producing the message and beep. Each #ifdef must be follwed by an #endif at the end of the code which is to be conditionally compiled. The macro name to be checked can be any valid macro name, and #ifdef blocks may be nested.
The counterpart to #ifdef is #ifndef, which causes the program to test to see if the macro has not been defined. It is needed in case the macro definition might or might not already be in a file that has been included. It is an error to try to define a macro that is already defined.
Macros can be conditionally defined.
The #if, #else, and #elif directives are also available, but are outside the scope of this document. Refer to a C reference manual for how to use them.
One of the standard C libraries included by KISS is the math library, which contains an extensive collection of math routines. The ones below are commonly used for robotics. For more information about what math functions are available, consult your C reference.
Library files provide standard C functions for interfacing with hardware on the robot controller board. These functions are written either in C or as assembly language drivers. Library files provide functions to do things like control motors and input sensors values.
KISS automatically has a selection of library files included every time it invokes the C compiler.
For convenience, a description of some of the more commonly used KIPR Link specific library functions follows.
digital(<port#>);
/* returns 0 if the switch attached to the port is open and
returns 1 if the switch is closed. Digital ports are numbered
8-15. Typically used for bumpers or limit switches. */
analog(<port#>);
/* returns the analog value of the port (a value in the range 0-255).
Analog ports are numbered 0-7. Light sensors and range sensors are
examples of sensors you would use in analog ports. */
msleep(<int_msecs>);
/* waits specified number of milliseconds */
beep();
/* causes a beep sound */
printf(<string>, <arg1>, <arg2>, ... );
/* prints <string>. If the string contains % codes then the <args>
after the string will be printed in place of the % codes in the
format specified by the code. %d prints a decimal number. %f
prints a floating point number. %c prints a character, %x or
%X prints an integer in hexadecimal. */
mav(<motor_#>, <vel>)
/* controls the motors. <motor_#> is an integer between 0 and 3.
<vel> is an integer between -1000 and 1000 where 0
means the motor is off and negative numbers run the motor in the
reverse direction */
fd(<motor_#>);
/* turns on the motor specified (direction is determined by plug
orientation */
bk(<motor_#>);
/* turns on the motor specified in the opposite direction from fd */
motor(<motor_#>, <motor_power>);
/* turns on the motor specified at the power level specified */
off(<motor_#>);
/* turns off the motor specified */
ao();
/* turns all motor ports off */
(alphabetic order)
a_button [Category: Sensors]
|
|
a_button_clicked [Category: Sensors]
|
|
accel_x [Category: Sensors]
|
|
accel_y [Category: Sensors]
|
|
accel_z [Category: Sensors]
|
|
alloff [Category: Motors]
|
|
analog
[Category: Sensors]
|
|
analog10
[Category: Sensors]
|
|
any_button [Category: Sensors]
|
|
ao
[Category: Motors]
|
|
b_button
[Category: Sensors]
|
|
b_button_clicked [Category: Sensors]
|
|
beep
[Category: Output]
|
|
bk
[Category: Motors]
|
|
block_motor_done [Category: Motors]
|
|
bmd [Category: Motors]
|
|
c_button [Category: Sensors]
|
|
c_button_clicked [Category: Sensors]
|
|
console_clear [Category: Output]
|
|
display_clear [Category: Output]
|
|
display_printf [Category: Output]
|
|
clear_motor_position_counter [Category: Motors]
|
|
digital [Category: Sensors]
|
|
disable_servo [Category: Servos]
|
|
disable_servos [Category: Servos]
|
|
enable_servo [Category: Servos]
|
|
enable_servos [Category: Servos]
|
|
extra_buttons_show [Category: Sensors]
|
|
extra_buttons_hide [Category: Sensors]
|
|
fd [Category: Motors]
|
|
freeze [Category: Motors]
|
|
get_analog_pullup [Category: Sensors]
|
|
get_extra_buttons_visible [Category: Sensors]
|
|
get_motor_done [Category: Motors]
|
|
get_motor_position_counter [Category: Motors]
|
|
get_pid_gains [Category: Motors]
|
|
get_servo_enabled [Category: Servos]
|
|
get_servo_position [Category: Servos]
|
|
mav [Category: Motors]
|
|
motor [Category: Motors]
|
|
move_at_velocity [Category: Motors]
|
|
move_relative_position [Category: Motors]
|
|
move_to_position [Category: Motors]
|
|
mrp [Category: Motors]
|
|
mtp [Category: Motors]
|
|
msleep [Category: Time]
|
|
off [Category: Motors]
|
|
power_level [Category: Sensor]
|
|
printf [Category: Output]
|
|
run_for [Category: Processes]
|
|
seconds [Category: Time]
|
|
set_a_button_text [Category: Sensors]
|
|
void set_analog_pullup(int port, int pullup); | set_analog_pullup [Category: Sensors]
|
set_b_button_text [Category: Sensors]
|
|
set_c_button_text [Category: Sensors]
|
|
set_digital_output [Category: Output]
|
|
void set_digital_pullup(int port, int pullup); | set_digital_pullup [Category: Sensors]
|
set_digital_value [Category: Output]
|
|
set_pid_gains [Category: Motors]
|
|
set_servo_position [Category: Servos]
|
|
set_x_button_text [Category: Sensors]
|
|
set_y_button_text [Category: Sensors]
|
|
set_z_button_text [Category: Sensors]
|
|
setpwm [Category: Motors]
|
|
side_button (or black_button) [Category: Sensors]
|
|
side_button_clicked [Category: Sensors]
|
|
thread_create [Category: Threads]
|
|
thread_destroy [Category: Threads]
|
|
thread_start [Category: Threads]
|
|
thread_wait [Category: Threads]
|
|
x_button [Category: Sensors]
|
|
x_button_clicked [Category: Sensors]
|
|
y_button [Category: Sensors]
|
|
y_button_clicked [Category: Sensors]
|
|
z_button [Category: Sensors]
|
|
z_button_clicked [Category: Sensors]
|
The Create library is automatically loaded whenever the KIPR Link is chosen as the target. The functions which update sensor data, and the connection functions return the requested information if they are successful and return a number greater than100,000 if there is some error. If an error is returned the error message is 100,000 + the Create Serial Interface Packet Number. For example a code of 100,007 indicates an error when requesting bumper or wheel drop sensor status. The functions starting get_create_ all take a single floating point argument which indicates that the sensor data should be updated if it is older than the argument. In other words, calling get_create_lbump(0.1) indicates that if the sensor data is less than 1/10th of a seconds old, then the cached value will be returned, but if it is older then a new value from the Create will be returned and cached. Calling with an argument of 0 will force a new value to be retrieved from the Create. Note that there is significant overhead in talking with the Create and so the lag times passed to these functions should not be smaller than needed. Values less than 0.05 should be avoided and larger times should be used for the angle and distance functions.
The movement function, with the exception of the script and block functions are all non-blocking. Movement functions (with the exception of create_stop()) only are sent to the Create if they represent a change from the previous movement command. For this reason, movement commands may be placed in tight loops without concern of overwhelming the serial connection. The Create's trajectory will continue until a different movement command is given. The script commands will execute without blocking and will finish when the goal is reached. The blocking spin function will not return until the goal has been reached.
The Create may also be used to play MIDI music. Up to sixteen 16 note songs may be loaded into the Create from the global variable matrix gc_song_array. See the Create Open Interface manual for details on note and duration codes.
create_connect [Category: Create Function]
|
create_disconnect [Category: Create Function]
|
create_start [Category: Create Function]
|
create_passive [Category: Create Function]
|
create_safe [Category: Create Function]
|
create_full [Category: Create Function]
|
create_spot [Category: Create Function]
|
create_cover [Category: Create Function]
|
create_demo [Category: Create Function]
|
create_cover_dock [Category: Create Function]
|
get_create_mode [Category:
Create Function]
|
get_create_lbump [Category:
Create Sensor Function]
|
get_create_rbump [Category:
Create Sensor Function]
|
get_create_lwdrop [Category:
Create Sensor Function]
|
get_create_cwdrop [Category:
Create Sensor Function]
|
get_create_rwdrop [Category:
Create Sensor Function]
|
get_create_wall [Category:
Create Sensor Function]
|
get_create_lcliff [Category:
Create Sensor Function]
|
get_create_lfcliff [Category:
Create Sensor Function]
|
get_create_rfcliff [Category:
Create Sensor Function]
|
get_create_rcliff [Category:
Create Sensor Function]
|
get_create_vwall [Category:
Create Sensor Function]
|
get_create_overcurrents [Category:
Create Sensor Function]
|
get_create_infrared [Category:
Create Sensor Function]
|
get_create_advance_button [Category:
Create Sensor Function]
|
get_create_play_button [Category:
Create Sensor Function]
|
get_create_distance [Category:
Create Sensor Function]
|
set_create_distance [Category:
Create Sensor Function]
|
get_create_normalized_angle [Category:
Create Sensor Function]
|
get_create_total_angle [Category:
Create Sensor Function]
|
set_create_normalized_angle [Category:
Create Sensor Function]
|
set_create_total_angle [Category:
Create Sensor Function]
|
get_create_battery_charging_state [Category:
Create Sensor Function]
|
get_create_battery_voltage [Category:
Create Sensor Function]
|
get_create_battery_current [Category:
Create Sensor Function]
|
get_create_battery_temp [Category:
Create Sensor Function]
|
get_create_battery_charge [Category:
Create Sensor Function]
|
get_create_battery_capacity [Category:
Create Sensor Function]
|
get_create_wall_amt [Category:
Create Sensor Function]
|
get_create_lcliff_amt [Category:
Create Sensor Function]
|
get_create_lfcliff_amt [Category:
Create Sensor Function]
|
get_create_rfcliff_amt [Category:
Create Sensor Function]
|
get_create_rcliff_amt [Category:
Create Sensor Function]
|
get_create_bay_DI [Category:
Create Sensor Function]
|
get_create_bay_AI [Category:
Create Sensor Function]
|
get_create_song_number [Category:
Create Sensor Function]
|
get_create_song_playing [Category:
Create Sensor Function]
|
get_create_number_of_stream_packets [Category:
Create Sensor Function]
|
get_create_requested_velocity [Category:
Create Sensor Function]
|
get_create_requested_radius [Category:
Create Sensor Function]
|
get_create_requested_right_velocity [Category:
Create Sensor Function]
|
get_create_requested_left_velocity [Category:
Create Sensor Function]
|
create_stop [Category: Create Movement Function]
|
create_drive [Category: Create Movement Function]
|
create_drive_straight [Category: Create Movement Function]
|
create_spin_CW [Category: Create Movement Function]
|
create_spin_CCW [Category: Create Movement Function]
|
create_drive_direct [Category: Create Movement Function]
|
create_advance_led [Category: Create Function]
|
create_play_led [Category: Create Function]
|
create_power_led [Category: Create Function]
|
create_load_song [Category: Create Function]
|
create_play_song [Category: Create Function]
|
camera_close [Category: Vision]
|
camera_load_config [Category: Vision]
|
camera_open [Category: Vision]
|
camera_open_device [Category: Vision]
|
camera_update [Category: Vision]
|
get_channel_count [Category: Vision]
|
get_code_num [Category: Vision]
|
get_object_area [Category: Vision]
|
get_object_bbox [Category: Vision]
|
get_object_center [Category: Vision]
|
get_object_centroid [Category: Vision]
|
get_object_confidence [Category: Vision]
|
get_object_count [Category: Vision]
|
get_object_data [Category: Vision]
|
get_object_data_length [Category: Vision]
|
|
|
|
|
|
|
|
|
|
|
|
Several functions have been created to assist programmers in creating programs that meet the basic Botball requirements of having robots wait until the starting lights come on and have their programs shut down after a specified amount of time. A typical Botball program will call the function
following any other set up routines it might be using. This function walks the robot operator through the light calibration routine, reports if the calibration was sucessful, and if it was, blocks until the light is turned on (if not, the process repeats after a brief pause). Normally this is immediately followed by a call to the Botball function
which will shutdown all motors and the Create once the specified time has expired, then terminate the program. If instead of completely killing the program you wish to do some activity for a specified time and then move on to other things, the function
will execute the requested function for the specified time and then halt that function if it has not already exited, allowing the statements following the run_for statement to be executed (such having servo motors maintain position).
The function
uses the vision system to look for a color target where the color described in the color channel a is completely surrounded by the color described by the color channel b. The addresses of to two integer variables, x & y, are also passed. If a suitable target is found in the current frame (this function does NOT perform a track_update() call) then the function returns 1 and the contents of the two variables x and y are changed to match the image pixel location of the centroid of the color blob associated with the inside of the target. If no target with the right colors is in view, then 0 is returned and x and y are not modified. If more than one target is in view, the one with the larger inner blob (as seen by the camera) is the one whose coordinates are returned. An example of use, where the inner color is on channel 3 and the outer on 2, might be:
The library functions for Botball are:
run_for(<time>, <function_name>);
/* Executes the function <function_name>
until it completes or the time <time> has expired. */
shut_down_in(<time>);
/* Returns immediately but starts a process
that at the end of <time> will kill the program and
turn off all motors and issue a stop command to the Create. */
target_cha_in_chb(<cha>, <chb>, <*x>, <*y>);
/* Looks for a target: a color surrounded by
another color where the channel number (0-3) for the inner color
is cha and the surrounding color channel is chb. If the target is
found then the function returns 1, otherwise 0. If a target is
found then the contents of the variables pointed to by x and y are
changed. */
wait_for_light(<port>);
/* Will run the operator through a
calibration procedure sampling light levels on a sensor plugged
into port number <port> when the starting light is on
and off and then blocks until it senses the starting light has
turned on. */
The term thread is short for the phrase "thread of execution", and represents a sequence of instructions to be managed by the system as it schedules processimg time among running processes. On a single processor machine, like the KIPR Link, the instructions running in separate threads appear to be operating in parallel. Each thread, once started, will continue until its process finishes or until it is forcibly terminated by another process using the thread_destroy(<thread_id>); statement. Each active thread gets a small amount of time in turn until all of its statements have been executed or it is forcibly terminated. If a thread's process cannot complete all of its statements before its turn is over, it is paused temporarily for the next thread to gets its share of time. This continues until all the active threads have gotten their slice of time and then it all repeats. The Link's processor is fast enough that from the user's viewpoint it appears that all of the active processes are running in parallel.
Functions running in threads can communicate with one another by reading and modifying global variables. The globals can be used as semaphores so that one process can signal another. Process IDs may also be stored in globals so that one process can destroy another one's thread if that is necessary program logic (think in terms of a process that is in an indefinite loop monitoring sensors, so it will never finish).
The library functions for controlling threads are:
thread_create(<function_name>);
/* thread_create creates a thread for running the specified function <function_name> and returns a value
of type thread that is the thread ID to be used for running the thread as an independent process */
thread_destroy(<thread_id>);
/* this will terminate (and remove) the thread specified by <thread_id>
(and stop its associated process) */
thread_start(<thread_id>);
/* this will activate the thread specified by <thread_id>
and run its associated process */
thread_wait(<thread_id>);
/* this will cause the program to wait until the specified <thread_id>
has finished - this can be used to sychronize two threads */
The image below is the front of the KIPR Link where all the sensor, motor, and servo ports can be found.
![]() Light Sensor (analog) |
![]() Infrared "Top Hat" Reflectance Sensor (analog) |
![]() Infrared "E.T." Distance Sensor (floating analog) |
![]() Touch Sensors (digital) |
![]() Infrared "Break Beam" Sensor (digital) |
|
![]() Analog Slider Sensor (analog) |
![]() Analog Pot Sensor (analog) |
![]() Small Reflectance Sensor (analog) |